package com.ceair.controller;

import com.ceair.aiService.AirfreightAssistant;
import com.ceair.aiService.MyAnnotationAssistant;
import com.ceair.aiService.MyClassicAssistant;
import com.ceair.entity.request.AirfreightAssistantReq;
import com.ceair.entity.result.Result;
import dev.langchain4j.community.model.dashscope.QwenChatModel;
import dev.langchain4j.community.model.dashscope.WanxImageModel;
import dev.langchain4j.data.document.Document;
import dev.langchain4j.data.document.loader.FileSystemDocumentLoader;
import dev.langchain4j.data.image.Image;
import dev.langchain4j.data.segment.TextSegment;
import dev.langchain4j.memory.chat.ChatMemoryProvider;
import dev.langchain4j.memory.chat.MessageWindowChatMemory;
import dev.langchain4j.model.embedding.EmbeddingModel;
import dev.langchain4j.model.ollama.OllamaChatModel;
import dev.langchain4j.model.openai.OpenAiChatModel;
import dev.langchain4j.model.output.Response;
import dev.langchain4j.service.AiServices;
import dev.langchain4j.store.embedding.EmbeddingStore;
import dev.langchain4j.store.embedding.EmbeddingStoreIngestor;
import io.micrometer.common.util.StringUtils;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.tags.Tag;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import java.util.Collections;
import java.util.List;
import java.util.Objects;

/**
 * @author wangbaohai
 * @ClassName TestController
 * @description: 测试接口
 * @date 2025年05月13日
 * @version: 1.0.0
 */
@RestController
@RequestMapping("/api/v1/test")
@RequiredArgsConstructor
@Slf4j
@Tag(name = "测试接口", description = "测试LangChain4j功能相关接口")
public class TestController {

    private final OpenAiChatModel openAiChatModel;
    private final OllamaChatModel ollamaChatModel;
    private final QwenChatModel qwenChatModel;
    private final MyAnnotationAssistant myAnnotationAssistant;
    private final AirfreightAssistant airfreightAssistant;
    private final EmbeddingModel embeddingModel;
    private final EmbeddingStore<TextSegment> embeddingStore;

    /**
     * 测试LangChain4j的deepseek功能
     * 向OpenAI模型发送预设问题并获取回答
     *
     * @return Result<String> 包含成功状态和模型回答结果的封装对象
     * 当调用成功时返回模型回答文本，失败时返回错误信息
     */
    @Operation(summary = "测试LangChain4j功能-deepseek")
    @PostMapping("/deepseek")
    public Result<String> deepseek() {
        // 向 模型 提问
        String answer = openAiChatModel.chat("请回答我你有什么用");
        return Result.success(answer);
    }

    /**
     * 测试LangChain4j的ollama功能
     * 向Ollama模型发送预设问题并获取回答
     *
     * @return Result<String> 包含成功状态和模型回答结果的封装对象
     * 当调用成功时返回模型回答文本，失败时返回错误信息
     */
    @Operation(summary = "测试LangChain4j功能-ollama")
    @PostMapping("/ollama")
    public Result<String> ollama() {
        // 向 模型 提问
        String answer = ollamaChatModel.chat("请说明你的模型名称以及回答我你有什么用");
        return Result.success(answer);
    }

    /**
     * 测试LangChain4j的Qwen功能
     * 向Qwen模型发送预设问题并获取回答
     *
     * @return Result<String> 包含成功状态和模型回答结果的封装对象
     * 当调用成功时返回模型回答文本，失败时返回错误信息
     */
    @Operation(summary = "测试LangChain4j功能-Qwen")
    @PostMapping("/Qwen")
    public Result<String> Qwen() {
        // 向 模型 提问
        String answer = qwenChatModel.chat("请说明你的模型名称以及回答我你的特长是什么");
        return Result.success(answer);
    }

    /**
     * 测试LangChain4j的Wanxiang功能
     * 向Wanxiang模型发送预设问题并获取回答
     *
     * @return Result<String> 包含成功状态和模型回答结果的封装对象
     * 当调用成功时返回模型回答文本，失败时返回错误信息
     */
    @Operation(summary = "测试LangChain4j功能-Wanxiang")
    @PostMapping("/wanxiang")
    public Result<String> wanxiang() {
        // 手动配置模型配置
        WanxImageModel wanxImageModel = WanxImageModel.builder()
                .modelName("wanx2.1-t2i-plus")
                .apiKey("sk-3cd002b68ce64b259d4535d9bbdb657e") // 这里需要你自己的 api-key
                .build();

        // 向 模型 提问
        Response<Image> response = wanxImageModel.generate("LOL英雄皮城女警正在参与5V5团战，战况十分激烈，正处于对抗高潮阶段");

        // 获取图片的路径
        String imageUrl = response.content().url().toString();
        return Result.success(imageUrl);
    }

    /**
     * 测试LangChain4j的MyClassicAssistant功能
     * 向MyClassicAssistant模型发送预设问题并获取回答
     *
     * @return Result<String> 包含成功状态和模型回答结果的封装对象
     * 当调用成功时返回模型回答文本，失败时返回错误信息
     */
    @Operation(summary = "测试LangChain4j功能-MyClassicAssistant")
    @PostMapping("/MyClassicAssistant")
    public Result<String> MyClassicAssistant() {
        // 定义一个 ChatMemoryProvider 函数式接口实例，用于根据 memoryId 创建聊天记忆窗口
        ChatMemoryProvider chatMemoryProvider = memoryId -> MessageWindowChatMemory.builder()
                // 设置记忆窗口的 ID（即会话 ID），用于区分不同用户的对话历史
                .id(memoryId)
                // 设置最大保存的消息数量为 10 条，超出后会自动移除最早的消息
                .maxMessages(10)
                // 构建 MessageWindowChatMemory 实例
                .build();

        // 使用 AiServices 构建器构建 AI 助手服务实例的代理
        MyClassicAssistant proxyMyClassicAssistant = AiServices
                // 指定 AI 助手的接口类型为 MyClassicAssistant
                .builder(MyClassicAssistant.class)
                // 设置使用的聊天模型为 qwenChatModel（通义千问模型）
                .chatModel(qwenChatModel)
                // 设置聊天记忆提供者，用于根据 memoryId 提供对应的聊天记忆
                .chatMemoryProvider(chatMemoryProvider)
                // 完成构建并返回 AI 助手实例
                .build();

        // 调用 AI 助手的 chat 方法发起对话
        // 第一个参数 1 是 memoryId，表示本次对话的记忆 ID（可用于后续对话状态保持）
        // 第二个参数是用户输入的提示信息，询问模型名称及其特长
        String answer1 = proxyMyClassicAssistant.chat(1, "记住我是王保海");
        String answer2 = proxyMyClassicAssistant.chat(1, "我是谁?");
        String answer3 = proxyMyClassicAssistant.chat(2, "我是谁?");

        // 将模型的回答封装到 Result 对象中并返回成功响应
        return Result.success(answer1 + "||" + answer2 + "||" + answer3);
    }

    /**
     * 测试LangChain4j的MyAnnotationAssistant功能
     * 向MyAnnotationAssistant模型发送预设问题并获取回答
     *
     * @return Result<String> 包含成功状态和模型回答结果的封装对象
     * 当调用成功时返回模型回答文本，失败时返回错误信息
     */
    @Operation(summary = "测试LangChain4j功能-MyAnnotationAssistant")
    @PostMapping("/MyAnnotationAssistant")
    public Result<String> MyAnnotationAssistant() {
        // 调用通过注入方式获取的 MyAnnotationAssistant 对象的 chat 方法发起对话
        // 第一个参数 1 是 memoryId，表示本次对话的记忆 ID（可用于后续对话状态保持）
        // 第二个参数是用户输入的提示信息，询问模型名称及其特长
        String answer1 = myAnnotationAssistant.chat(1, "记住我是王保海");
        String answer2 = myAnnotationAssistant.chat(1, "我是谁?");
        String answer3 = myAnnotationAssistant.chat(2, "我是谁?");

        // 将模型的回答封装到 Result 对象中并返回成功响应
        return Result.success(answer1 + "||" + answer2 + "||" + answer3);
    }

    /**
     * 测试LangChain4j的AirfreightAssistant功能
     * 向AirfreightAssistant模型发送预设问题并获取回答
     *
     * @return Result<String> 包含成功状态和模型回答结果的封装对象
     * 当调用成功时返回模型回答文本，失败时返回错误信息
     */
    @Operation(summary = "测试LangChain4j功能-航空货运助手")
    @Parameter(name = "airfreightAssistantReq", description = "航空货运助手请求参数", required = true)
    @PostMapping("/AirfreightAssistant")
    public Result<String> AirfreightAssistant(@RequestBody AirfreightAssistantReq airfreightAssistantReq) {
        if (Objects.isNull(airfreightAssistantReq) || StringUtils.isEmpty(airfreightAssistantReq.getMessage())) {
            return Result.error("参数错误");
        }

        // 调用通过注入方式获取的 AirfreightAssistant 对象的 chat 方法发起对话
        String answer = airfreightAssistant.chat(airfreightAssistantReq.getMemoryId(),
                airfreightAssistantReq.getMessage());

        return Result.success(answer);
    }

    /**
     * 测试LangChain4j的AirfreightAssistant功能
     * 向AirfreightAssistant模型发送预设问题并获取回答
     *
     * @return Result<String> 包含成功状态和模型回答结果的封装对象
     * 当调用成功时返回模型回答文本，失败时返回错误信息
     */
    @Operation(summary = "测试LangChain4j功能-上传文件到向量数据库")
    @PostMapping("/uploadFiles")
    public Result<String> uploadFiles() {
        // 先简单的从本地文件夹中读取文件并使用默认的文档解析器对文档进行解析
        Document document = FileSystemDocumentLoader.loadDocument("D:/CSDN博客内容搬家服务协议.md");
        List<Document> documents = Collections.singletonList(document);

        //文本向量化并存入向量数据库：将每个片段进行向量化，得到一个嵌入向量
        EmbeddingStoreIngestor.builder()
                .embeddingModel(embeddingModel)
                .embeddingStore(embeddingStore)
                .build()
                .ingest(documents);

        return Result.success("上传文件到向量数据库成功");
    }

}
